#include <iostream>
#include <memory>

//Product Interface
class Product
{
public:
    virtual ~Product() = default; 
    virtual std::string Operation() const = 0;
};

//Concrete productA
class ConcreteProductA : public Product{
public:
    std::string Operation() const override{
        return "Result of ConcreeProductA";
    }
};

//Concrete productB
class ConcreteProductB : public Product{
public:
    std::string Operation() const override{
        return "Result of ConcreeProductB";
    }
};

//Creator class(Factory)
class Creator 
{
public:
    virtual ~Creator() = default;
    virtual std::unique_ptr<Product> FactoryMethod() const = 0;

    std::string SomeOperation() const
    {
        //Create product
        std::unique_ptr<Product> product = this->FactoryMethod();
        //Use product
        std::string result = "Creator: The same creator's code has just worked with" + product->Operation();
        return result;
    }
};

//Concrete creatorA
class ConcreteCreatorA : public Creator{
    std::unique_ptr<Product> FactoryMethod() const override{
        return std::make_unique<ConcreteProductA> ();
    }
};
//Concrete creatorA
class ConcreteCreatorB : public Creator{
    std::unique_ptr<Product> FactoryMethod() const override{
        return std::make_unique<ConcreteProductB> ();
    }
};
//Client
void ClientCode(const Creator& creator){
    std::cout << "Client: I'm not aware of the creator's class, but it still works" 
    << creator.SomeOperation() << std::endl;
}

int main()
{
    std::cout << "App: Launched with the ConcreteCreatorA.\n";
    std::unique_ptr<Creator> creatorA = std::make_unique<ConcreteCreatorA>();
    ClientCode(*creatorA);
    
    std::cout << "\nApp: Launched with the ConcreteCreatorB.\n";
    std::unique_ptr<Creator> creatorB = std::make_unique<ConcreteCreatorB>();
    ClientCode(*creatorB);
}
